package com.ymt.bpm.util;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTCreationException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.ymt.bpm.model.PUser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.UnsupportedEncodingException;
import java.util.Date;

/**
 * Created by Johnny on 2017/11/26.
 */
public class JwtUtil {

    private static Logger log = LoggerFactory.getLogger(JwtUtil.class);

    private static final String SECRET = "BPMYUN_SECRET_2017";
    private static final String ISSUER = "BPMYUN2017";
    private static Algorithm algorithm = null;
    private static JWTVerifier verifier = null;

    static {
        try {
            algorithm = Algorithm.HMAC256(SECRET);
            verifier = JWT.require(algorithm)
                    .withIssuer(ISSUER)
                    .build();
        } catch (UnsupportedEncodingException e){
            e.printStackTrace();
        }
    }

    public static String token(Integer tenantIdInt, String loginName, String displayName, String lang, long time, long expires) {
        try {
            JWTCreator.Builder builder = JWT.create()
                    .withIssuer(ISSUER)
                    .withSubject(loginName)
                    .withClaim(Const.DISPLAYNAME, displayName)
                    .withExpiresAt(new Date(time + expires));
            if (tenantIdInt!=null && tenantIdInt>0) {
                builder.withClaim(Const.TENANT_ID, String.valueOf(tenantIdInt));
            }
            builder.withClaim(Const.LANG, lang!=null && lang.length()>0 ? lang : Const.LANG_ZH);
            //记录server端过期时间，便于刷新
            builder.withClaim(Const.JWT_EXPIRES, String.valueOf(expires));
            builder.withClaim(Const.JWT_SERVER_EXPIRES, String.valueOf(System.currentTimeMillis() + expires));
            String token = builder.sign(algorithm);
            return token;
        } catch (JWTCreationException e){
            e.printStackTrace();
            return null;
        }
    }

    public static DecodedJWT decode(String token) {
        if (token==null || token.length()==0) {
            return null;
        }
        try {
            if (log.isTraceEnabled()) {
                log.trace("Token to be decoded:" + token);
            }
            DecodedJWT jwt = verifier.verify(token);
//            System.out.println(jwt.getHeader());
//            System.out.println(jwt.getPayload());
//            System.out.println(jwt.getSignature());
//            System.out.println(jwt.getToken());
//            System.out.println(jwt.getAlgorithm());
//            System.out.println(jwt.getAudience());
//            System.out.println(jwt.getContentType());
//            System.out.println(jwt.getClaims());
//            System.out.println(jwt.getSubject());
//            System.out.println(jwt.getType());
//            System.out.println(jwt.getNotBefore());
            return jwt;
        } catch (TokenExpiredException e1){
            if (log.isTraceEnabled()) {
                log.trace("Token expired:" + e1.getMessage());
            }
            return null;
        } catch (JWTVerificationException e){
            if (log.isTraceEnabled()) {
                log.trace("Token verify failed:", e);
            }
            return null;
        }
    }

    public static String decodeLoginName(String token) {
        try {
            DecodedJWT jwt = decode(token);
            return jwt.getSubject();
        } catch (TokenExpiredException e1){
            if (log.isTraceEnabled()) {
                log.trace("Token expired:" + e1.getMessage());
            }
            return null;
        } catch (JWTVerificationException e){
            if (log.isTraceEnabled()) {
                log.trace("Token verify failed:", e);
            }
            return null;
        }
    }

    /**
     *
     * @param jwt
     * @param checkBefore 离过期还有多少时间之内
     * @return
     */
    public static boolean aboutToExpire(DecodedJWT jwt, long checkBefore) {
        Long se = Long.parseLong(jwt.getClaim(Const.JWT_SERVER_EXPIRES).asString());
        long cur = System.currentTimeMillis();
        if (cur>se && cur-se > checkBefore) {
            return true;
        }
        return false;
    }

    /**
     * 刷新
     * @param jwt
     * @return
     */
    public static String refreshTokenExpires(DecodedJWT jwt) {
        try {
            long expires = Long.parseLong(jwt.getClaim(Const.JWT_EXPIRES).asString());
            JWTCreator.Builder builder = JWT.create()
                    .withIssuer(ISSUER)
                    .withSubject(jwt.getSubject())
                    .withClaim(Const.DISPLAYNAME, jwt.getClaim(Const.DISPLAYNAME).asString())
                    .withExpiresAt(new Date(jwt.getExpiresAt().getTime() + expires));
            Claim tenantIdClaim = jwt.getClaim(Const.TENANT_ID);
            if (tenantIdClaim!=null) {
                builder.withClaim(Const.TENANT_ID, tenantIdClaim.asString());
            }
            builder.withClaim(Const.LANG, jwt.getClaim(Const.LANG).asString());
            //记录server端过期时间，便于刷新
            builder.withClaim(Const.JWT_EXPIRES, String.valueOf(expires));
            builder.withClaim(Const.JWT_SERVER_EXPIRES, String.valueOf(System.currentTimeMillis() + expires));
            String token = builder.sign(algorithm);
            return token;
        } catch (JWTCreationException e){
            e.printStackTrace();
            return null;
        }
    }

    public static void main(String[] args) {
        String token = token(1, "zhangsan", "张三", "zh", System.currentTimeMillis(), 1000L);
        System.out.println(token);
        System.out.println(decodeLoginName(token));
        DecodedJWT jwt = decode(token);
        System.out.println(jwt.getClaim(Const.DISPLAYNAME).asString());
        System.out.println(jwt.getClaim(Const.TENANT_ID).asString());
    }

}
